/*
 *
 *    Copyright (c) 2022 Project CHIP Authors
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

// THIS FILE IS GENERATED BY ZAP

#include <cinttypes>
#include <cstdint>

#include <app-common/zap-generated/callback.h>
#include <app-common/zap-generated/cluster-objects.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app-common/zap-generated/ids/Commands.h>
#include <app/CommandHandler.h>
#include <app/InteractionModelEngine.h>
#include <app/util/util.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/support/TypeTraits.h>

namespace chip
{
namespace app
{

	// Cluster specific command parsing

	namespace Clusters
	{

		namespace Groups
		{

			Protocols::InteractionModel::Status
			DispatchServerCommand(CommandHandler *apCommandObj, const ConcreteCommandPath &aCommandPath,
					      TLV::TLVReader &aDataTlv)
			{
				CHIP_ERROR TLVError = CHIP_NO_ERROR;
				bool wasHandled = false;
				{
					switch (aCommandPath.mCommandId) {
					case Commands::AddGroup::Id: {
						Commands::AddGroup::DecodableType commandData;
						TLVError = commandData.Decode(aDataTlv,
									      apCommandObj->GetAccessingFabricIndex());
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled = emberAfGroupsClusterAddGroupCallback(
								apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::ViewGroup::Id: {
						Commands::ViewGroup::DecodableType commandData;
						TLVError = commandData.Decode(aDataTlv,
									      apCommandObj->GetAccessingFabricIndex());
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled = emberAfGroupsClusterViewGroupCallback(
								apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::GetGroupMembership::Id: {
						Commands::GetGroupMembership::DecodableType commandData;
						TLVError = commandData.Decode(aDataTlv,
									      apCommandObj->GetAccessingFabricIndex());
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled = emberAfGroupsClusterGetGroupMembershipCallback(
								apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::RemoveGroup::Id: {
						Commands::RemoveGroup::DecodableType commandData;
						TLVError = commandData.Decode(aDataTlv,
									      apCommandObj->GetAccessingFabricIndex());
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled = emberAfGroupsClusterRemoveGroupCallback(
								apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::RemoveAllGroups::Id: {
						Commands::RemoveAllGroups::DecodableType commandData;
						TLVError = commandData.Decode(aDataTlv,
									      apCommandObj->GetAccessingFabricIndex());
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled = emberAfGroupsClusterRemoveAllGroupsCallback(
								apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::AddGroupIfIdentifying::Id: {
						Commands::AddGroupIfIdentifying::DecodableType commandData;
						TLVError = commandData.Decode(aDataTlv,
									      apCommandObj->GetAccessingFabricIndex());
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled = emberAfGroupsClusterAddGroupIfIdentifyingCallback(
								apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					default: {
						// Unrecognized command ID, error status will apply.
						ChipLogError(Zcl,
							     "Unknown command " ChipLogFormatMEI
							     " for cluster " ChipLogFormatMEI,
							     ChipLogValueMEI(aCommandPath.mCommandId),
							     ChipLogValueMEI(aCommandPath.mClusterId));
						return Protocols::InteractionModel::Status::UnsupportedCommand;
					}
					}
				}

				if (CHIP_NO_ERROR != TLVError || !wasHandled) {
					ChipLogProgress(Zcl, "Failed to dispatch command, TLVError=%" CHIP_ERROR_FORMAT,
							TLVError.Format());
					return Protocols::InteractionModel::Status::InvalidCommand;
				}

				// We use success as a marker that no special handling is required
				// This is to avoid having a std::optional which uses slightly more code.
				return Protocols::InteractionModel::Status::Success;
			}

		} // namespace Groups

		namespace IcdManagement
		{

			Protocols::InteractionModel::Status
			DispatchServerCommand(CommandHandler *apCommandObj, const ConcreteCommandPath &aCommandPath,
					      TLV::TLVReader &aDataTlv)
			{
				CHIP_ERROR TLVError = CHIP_NO_ERROR;
				bool wasHandled = false;
				{
					switch (aCommandPath.mCommandId) {
					case Commands::RegisterClient::Id: {
						Commands::RegisterClient::DecodableType commandData;
						TLVError = commandData.Decode(aDataTlv,
									      apCommandObj->GetAccessingFabricIndex());
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled = emberAfIcdManagementClusterRegisterClientCallback(
								apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::UnregisterClient::Id: {
						Commands::UnregisterClient::DecodableType commandData;
						TLVError = commandData.Decode(aDataTlv,
									      apCommandObj->GetAccessingFabricIndex());
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled =
								emberAfIcdManagementClusterUnregisterClientCallback(
									apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::StayActiveRequest::Id: {
						Commands::StayActiveRequest::DecodableType commandData;
						TLVError = DataModel::Decode(aDataTlv, commandData);
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled =
								emberAfIcdManagementClusterStayActiveRequestCallback(
									apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					default: {
						// Unrecognized command ID, error status will apply.
						ChipLogError(Zcl,
							     "Unknown command " ChipLogFormatMEI
							     " for cluster " ChipLogFormatMEI,
							     ChipLogValueMEI(aCommandPath.mCommandId),
							     ChipLogValueMEI(aCommandPath.mClusterId));
						return Protocols::InteractionModel::Status::UnsupportedCommand;
					}
					}
				}

				if (CHIP_NO_ERROR != TLVError || !wasHandled) {
					ChipLogProgress(Zcl, "Failed to dispatch command, TLVError=%" CHIP_ERROR_FORMAT,
							TLVError.Format());
					return Protocols::InteractionModel::Status::InvalidCommand;
				}

				// We use success as a marker that no special handling is required
				// This is to avoid having a std::optional which uses slightly more code.
				return Protocols::InteractionModel::Status::Success;
			}

		} // namespace IcdManagement

		namespace OtaSoftwareUpdateRequestor
		{

			Protocols::InteractionModel::Status
			DispatchServerCommand(CommandHandler *apCommandObj, const ConcreteCommandPath &aCommandPath,
					      TLV::TLVReader &aDataTlv)
			{
				CHIP_ERROR TLVError = CHIP_NO_ERROR;
				bool wasHandled = false;
				{
					switch (aCommandPath.mCommandId) {
					case Commands::AnnounceOTAProvider::Id: {
						Commands::AnnounceOTAProvider::DecodableType commandData;
						TLVError = DataModel::Decode(aDataTlv, commandData);
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled =
								emberAfOtaSoftwareUpdateRequestorClusterAnnounceOTAProviderCallback(
									apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					default: {
						// Unrecognized command ID, error status will apply.
						ChipLogError(Zcl,
							     "Unknown command " ChipLogFormatMEI
							     " for cluster " ChipLogFormatMEI,
							     ChipLogValueMEI(aCommandPath.mCommandId),
							     ChipLogValueMEI(aCommandPath.mClusterId));
						return Protocols::InteractionModel::Status::UnsupportedCommand;
					}
					}
				}

				if (CHIP_NO_ERROR != TLVError || !wasHandled) {
					ChipLogProgress(Zcl, "Failed to dispatch command, TLVError=%" CHIP_ERROR_FORMAT,
							TLVError.Format());
					return Protocols::InteractionModel::Status::InvalidCommand;
				}

				// We use success as a marker that no special handling is required
				// This is to avoid having a std::optional which uses slightly more code.
				return Protocols::InteractionModel::Status::Success;
			}

		} // namespace OtaSoftwareUpdateRequestor

		namespace OnOff
		{

			Protocols::InteractionModel::Status
			DispatchServerCommand(CommandHandler *apCommandObj, const ConcreteCommandPath &aCommandPath,
					      TLV::TLVReader &aDataTlv)
			{
				CHIP_ERROR TLVError = CHIP_NO_ERROR;
				bool wasHandled = false;
				{
					switch (aCommandPath.mCommandId) {
					case Commands::Off::Id: {
						Commands::Off::DecodableType commandData;
						TLVError = DataModel::Decode(aDataTlv, commandData);
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled = emberAfOnOffClusterOffCallback(
								apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::On::Id: {
						Commands::On::DecodableType commandData;
						TLVError = DataModel::Decode(aDataTlv, commandData);
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled = emberAfOnOffClusterOnCallback(
								apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::Toggle::Id: {
						Commands::Toggle::DecodableType commandData;
						TLVError = DataModel::Decode(aDataTlv, commandData);
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled = emberAfOnOffClusterToggleCallback(
								apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::OffWithEffect::Id: {
						Commands::OffWithEffect::DecodableType commandData;
						TLVError = DataModel::Decode(aDataTlv, commandData);
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled = emberAfOnOffClusterOffWithEffectCallback(
								apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::OnWithRecallGlobalScene::Id: {
						Commands::OnWithRecallGlobalScene::DecodableType commandData;
						TLVError = DataModel::Decode(aDataTlv, commandData);
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled = emberAfOnOffClusterOnWithRecallGlobalSceneCallback(
								apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::OnWithTimedOff::Id: {
						Commands::OnWithTimedOff::DecodableType commandData;
						TLVError = DataModel::Decode(aDataTlv, commandData);
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled = emberAfOnOffClusterOnWithTimedOffCallback(
								apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					default: {
						// Unrecognized command ID, error status will apply.
						ChipLogError(Zcl,
							     "Unknown command " ChipLogFormatMEI
							     " for cluster " ChipLogFormatMEI,
							     ChipLogValueMEI(aCommandPath.mCommandId),
							     ChipLogValueMEI(aCommandPath.mClusterId));
						return Protocols::InteractionModel::Status::UnsupportedCommand;
					}
					}
				}

				if (CHIP_NO_ERROR != TLVError || !wasHandled) {
					ChipLogProgress(Zcl, "Failed to dispatch command, TLVError=%" CHIP_ERROR_FORMAT,
							TLVError.Format());
					return Protocols::InteractionModel::Status::InvalidCommand;
				}

				// We use success as a marker that no special handling is required
				// This is to avoid having a std::optional which uses slightly more code.
				return Protocols::InteractionModel::Status::Success;
			}

		} // namespace OnOff

		namespace ThreadNetworkDiagnostics
		{

			Protocols::InteractionModel::Status
			DispatchServerCommand(CommandHandler *apCommandObj, const ConcreteCommandPath &aCommandPath,
					      TLV::TLVReader &aDataTlv)
			{
				CHIP_ERROR TLVError = CHIP_NO_ERROR;
				bool wasHandled = false;
				{
					switch (aCommandPath.mCommandId) {
					case Commands::ResetCounts::Id: {
						Commands::ResetCounts::DecodableType commandData;
						TLVError = DataModel::Decode(aDataTlv, commandData);
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled =
								emberAfThreadNetworkDiagnosticsClusterResetCountsCallback(
									apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					default: {
						// Unrecognized command ID, error status will apply.
						ChipLogError(Zcl,
							     "Unknown command " ChipLogFormatMEI
							     " for cluster " ChipLogFormatMEI,
							     ChipLogValueMEI(aCommandPath.mCommandId),
							     ChipLogValueMEI(aCommandPath.mClusterId));
						return Protocols::InteractionModel::Status::UnsupportedCommand;
					}
					}
				}

				if (CHIP_NO_ERROR != TLVError || !wasHandled) {
					ChipLogProgress(Zcl, "Failed to dispatch command, TLVError=%" CHIP_ERROR_FORMAT,
							TLVError.Format());
					return Protocols::InteractionModel::Status::InvalidCommand;
				}

				// We use success as a marker that no special handling is required
				// This is to avoid having a std::optional which uses slightly more code.
				return Protocols::InteractionModel::Status::Success;
			}

		} // namespace ThreadNetworkDiagnostics

		namespace TimeSynchronization
		{

			Protocols::InteractionModel::Status
			DispatchServerCommand(CommandHandler *apCommandObj, const ConcreteCommandPath &aCommandPath,
					      TLV::TLVReader &aDataTlv)
			{
				CHIP_ERROR TLVError = CHIP_NO_ERROR;
				bool wasHandled = false;
				{
					switch (aCommandPath.mCommandId) {
					case Commands::SetUTCTime::Id: {
						Commands::SetUTCTime::DecodableType commandData;
						TLVError = DataModel::Decode(aDataTlv, commandData);
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled =
								emberAfTimeSynchronizationClusterSetUTCTimeCallback(
									apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::SetTrustedTimeSource::Id: {
						Commands::SetTrustedTimeSource::DecodableType commandData;
						TLVError = commandData.Decode(aDataTlv,
									      apCommandObj->GetAccessingFabricIndex());
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled =
								emberAfTimeSynchronizationClusterSetTrustedTimeSourceCallback(
									apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::SetTimeZone::Id: {
						Commands::SetTimeZone::DecodableType commandData;
						TLVError = DataModel::Decode(aDataTlv, commandData);
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled =
								emberAfTimeSynchronizationClusterSetTimeZoneCallback(
									apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					case Commands::SetDSTOffset::Id: {
						Commands::SetDSTOffset::DecodableType commandData;
						TLVError = DataModel::Decode(aDataTlv, commandData);
						if (TLVError == CHIP_NO_ERROR) {
							wasHandled =
								emberAfTimeSynchronizationClusterSetDSTOffsetCallback(
									apCommandObj, aCommandPath, commandData);
						}
						break;
					}
					default: {
						// Unrecognized command ID, error status will apply.
						ChipLogError(Zcl,
							     "Unknown command " ChipLogFormatMEI
							     " for cluster " ChipLogFormatMEI,
							     ChipLogValueMEI(aCommandPath.mCommandId),
							     ChipLogValueMEI(aCommandPath.mClusterId));
						return Protocols::InteractionModel::Status::UnsupportedCommand;
					}
					}
				}

				if (CHIP_NO_ERROR != TLVError || !wasHandled) {
					ChipLogProgress(Zcl, "Failed to dispatch command, TLVError=%" CHIP_ERROR_FORMAT,
							TLVError.Format());
					return Protocols::InteractionModel::Status::InvalidCommand;
				}

				// We use success as a marker that no special handling is required
				// This is to avoid having a std::optional which uses slightly more code.
				return Protocols::InteractionModel::Status::Success;
			}

		} // namespace TimeSynchronization

	} // namespace Clusters

	void DispatchSingleClusterCommand(const ConcreteCommandPath &aCommandPath, TLV::TLVReader &aReader,
					  CommandHandler *apCommandObj)
	{
		Protocols::InteractionModel::Status errorStatus = Protocols::InteractionModel::Status::Success;

		switch (aCommandPath.mClusterId) {
		case Clusters::Groups::Id:
			errorStatus = Clusters::Groups::DispatchServerCommand(apCommandObj, aCommandPath, aReader);
			break;
		case Clusters::IcdManagement::Id:
			errorStatus =
				Clusters::IcdManagement::DispatchServerCommand(apCommandObj, aCommandPath, aReader);
			break;
		case Clusters::OtaSoftwareUpdateRequestor::Id:
			errorStatus = Clusters::OtaSoftwareUpdateRequestor::DispatchServerCommand(
				apCommandObj, aCommandPath, aReader);
			break;
		case Clusters::OnOff::Id:
			errorStatus = Clusters::OnOff::DispatchServerCommand(apCommandObj, aCommandPath, aReader);
			break;
		case Clusters::ThreadNetworkDiagnostics::Id:
			errorStatus = Clusters::ThreadNetworkDiagnostics::DispatchServerCommand(apCommandObj,
												aCommandPath, aReader);
			break;
		case Clusters::TimeSynchronization::Id:
			errorStatus = Clusters::TimeSynchronization::DispatchServerCommand(apCommandObj, aCommandPath,
											   aReader);
			break;
		default:
			ChipLogError(Zcl, "Unknown cluster " ChipLogFormatMEI,
				     ChipLogValueMEI(aCommandPath.mClusterId));
			errorStatus = Protocols::InteractionModel::Status::UnsupportedCluster;
			break;
		}

		if (errorStatus != Protocols::InteractionModel::Status::Success) {
			apCommandObj->AddStatus(aCommandPath, errorStatus);
		}
	}

} // namespace app
} // namespace chip
